package gorm

import (
	"context"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/ini.v1"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"io"
	"log"
	"os"
	"time"
)

type GormZapLogger struct {
	logger *zap.Logger
}

func NewGormZapLogger(logFile io.Writer) *GormZapLogger {
	encoderConfig := zapcore.EncoderConfig{
		TimeKey:        "time",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller",
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.LowercaseLevelEncoder,
		EncodeTime:     zapcore.ISO8601TimeEncoder,
		EncodeDuration: zapcore.SecondsDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	}

	newCore := zapcore.NewCore(
		zapcore.NewJSONEncoder(encoderConfig),
		zapcore.AddSync(logFile),
		zapcore.DebugLevel,
	)

	logger := zap.New(newCore)
	defer logger.Sync()

	return &GormZapLogger{
		logger: logger,
	}
}

func (l *GormZapLogger) LogMode(level logger.LogLevel) logger.Interface {
	return l
}

func (l *GormZapLogger) Info(ctx context.Context, msg string, data ...interface{}) {
	l.logger.Info(msg, convertToFields(data)...)
}

func (l *GormZapLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
	l.logger.Warn(msg, convertToFields(data)...)
}

func (l *GormZapLogger) Error(ctx context.Context, msg string, data ...interface{}) {
	l.logger.Error(msg, convertToFields(data)...)
}

func (l *GormZapLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
	sql, rows := fc()
	l.logger.Debug("SQL", zap.String("sql", sql), zap.Int64("rows_affected", rows), zap.Duration("duration", time.Since(begin)), zap.Error(err))
}

func convertToFields(data []interface{}) []zap.Field {
	fields := make([]zap.Field, 0, len(data))
	for i := 0; i < len(data); i += 2 {
		key, ok := data[i].(string)
		if !ok {
			continue
		}
		if i+1 >= len(data) {
			break
		}
		value := data[i+1]
		switch v := value.(type) {
		case string:
			fields = append(fields, zap.String(key, v))
		case int:
			fields = append(fields, zap.Int(key, v))
		case int64:
			fields = append(fields, zap.Int64(key, v))
		case float64:
			fields = append(fields, zap.Float64(key, v))
		case bool:
			fields = append(fields, zap.Bool(key, v))
		default:
			fields = append(fields, zap.Any(key, v))
		}
	}
	return fields
}

var Db *gorm.DB
var logFile *os.File // 共享日志文件

func init() {
	// 打开共享日志文件
	var err error
	logFile, err = os.OpenFile("db.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalf("Failed to open log file: %v", err)
	}
}

func InitGormDB() {
	// 从配置文件中读取数据库配置
	cfg, err := ini.Load("./Config/config.ini")
	if err != nil {
		log.Fatalf("Failed to load config: %v", err)
	}

	// 构建数据库连接字符串

	dsn := cfg.Section("mysql").Key("username").String() + ":" +
		cfg.Section("mysql").Key("password").String() + "@" +
		cfg.Section("mysql").Key("host").String() + "/" +
		cfg.Section("mysql").Key("database").String() +
		"?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai"

	// 输入数据库连接信息
	log.Println("gorm 连接数据:", dsn)

	// 创建自定义 GORM 日志记录器
	gormLogger := NewGormZapLogger(logFile)

	// 打开数据库连接
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger: gormLogger,
	})
	if err != nil {
		// 使用 zap 记录错误
		gormLogger.logger.Error("Error opening database", zap.Error(err))
		return
	}

	Db = db
}
